""" A python interface for simple matrix vector operations 

Supports instantiating, reinitializing and basic vector algebra.

Example:
--------

>>> v = Vector(1,0,0)

>>> v.set(1,2,2)

>>> v1 = v + v
>>> v2 = v*2
 
Cross products are taken as:
v = v1.cross(v2) ---> v1 X v2

"""

#Numpy for arrays
import numpy as np

class Point(object):
    """A Point represents well, a vertex in 3D.

    Data Attributes:
    ----------------

    x, y, z : float
         coordinate positions

    """

    def __init__(self, x=0, y=0, z=0):
        self.x = float(x)
        self.y = float(y)
        self.z = float(z)

############################################################################
# `Vector` class
############################################################################
class Vector(Point):
    """" A three dimensional vector supporting basic operations"""
    
    def __init__(self, x = 0.0, y = 0.0, z=0.0):
        """ Default constructor """

        Point.__init__(self, x, y, z)
        self.w = 1.0
        
    ########################################################################
    #`Vector` interface
    ########################################################################

    def norm(self):
        """ Return the norm of the point """
        return np.sqrt(self.x*self.x + self.y*self.y + self.z*self.z)

    def asvector(self):
        """ Return a numpy vector for the object """
        return  np.array([self.x, self.y, self.z, self.w])

    def fromarray(self, vec):
        return Vector(vec[0], vec[1], vec[2], vec[3])
    
    def normalize(self):
        """ Normalize the vector """
        norm = self.norm()
        self.x /= norm
        self.y /= norm
        self.z /= norm

    def set(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

    def dot(self, vec):
        """ Inner product with another vector """
        return self.x*vec.x + self.y*vec.y + self.z*vec.z

    def cross(self, vec):
        """ Return the cross product with another vector """
        return Vector(self.y*vec.z - self.z*vec.y,
                      self.z*vec.x - self.x*vec.z,
                      self.x*vec.y - self.y*vec.x)

    def transform(self, matrix):
        return self.fromvector( np.dot(matrix, self.asvector()) )

    ########################################################################
    #`object` interface
    ########################################################################
    
    def __str__(self):
        return '(%f, %f, %f)'%(self.x, self.y, self.z)

    def __repr__(self):
        return 'Vector(%g, %g, %g)'%(self.x, self.y, self.z)

    def __add__(self, vec):
        return Vector(self.x+vec.x, self.y+vec.y, self.z+vec.z)

    def __sub__(self, vec):
        return Vector(self.x-vec.x, self.y-vec.y, self.z-vec.z)

    def __mul__(self, a):
        return Vector(self.x*a, self.y*a, self.z*a)

    def __div__(self, a):
        return Vector(self.x/a, self.y/a, self.z/a)

    def __eq__(self, other):
        return (self.x==other.x) and (self.y==other.y) and (self.z==other.z)
    
    def __neg__(self):
        return Vector(-self.x, -self.y, -self.z)
    
    def __iadd__(self, vec):
        self.x += vec.x
        self.y += vec.y
        self.z += vec.z
        return self

    def __isub__(self, p):
        self.x -= p.x
        self.y -= p.y
        self.z -= p.z
        return self

    def __imul__(self, m):
        self.x *= m
        self.y *= m
        self.z *= m
        return self

    def __idiv__(self, m):
        self.x /= m
        self.y /= m
        self.z /= m
        return self

#End of Vector Class

class Vertex(Vector):

    def reflect(self, va, tangent):
        """Reflect the vertex about a given vector"""
        va_p = self - va

        # projected distance along the vector
        l = va_p.dot( tangent )

        # projected point pl
        pl = va + (tangent * l)

        # return the reflected point
        return pl + (pl - self)
